﻿using System;
using System.Collections.Generic;
using System.Linq;
using DesignByContract;
using MadMediaManager.WPFSupport.Events;
using MadMediaManager.WPFSupport.Interfaces.Services;
using MadMediaManager.WPFSupport.Interfaces.Views;

namespace MadMediaManager.WPFSupport.Services
{
    public class ViewManager : IViewManager
    {
        private INavigableView _currentView;
        private readonly IEventPublisher _eventPublisher;
        private readonly IViewFactory _viewFactory;
        private readonly IContentRegionOwner _contentRegionOwner;
        private readonly ITransitioner _transitioner;
        private readonly Dictionary<ViewKey, INavigableView> _views = new Dictionary<ViewKey, INavigableView>();
        private readonly object _lockObject = new object();

        public ViewManager(IViewFactory viewFactory, IEventPublisher eventPublisher, ITransitioner transitioner, IContentRegionOwner contentRegionOwner)
        {
            Check.Require(viewFactory != null, "viewFactory cannot be null");
            Check.Require(contentRegionOwner != null, "homeView cannot be null");
            Check.Require(transitioner != null, "transitioner cannot be null");
            Check.Require(eventPublisher != null, "eventPublisher cannot be null");

            _eventPublisher = eventPublisher;
            _viewFactory = viewFactory;
            _contentRegionOwner = contentRegionOwner;
            _transitioner = transitioner;
        }

        private Action _homeTransitionAction;
        public ViewManager SetHomeView<TView>(ViewKey<TView> homeViewKey, TransitionType transition)
            where TView : INavigableView
        {
            _homeTransitionAction = () => TransitionTo(homeViewKey, transition);
            return this;
        }

        public void TransitionTo<TView>(ViewKey<TView> viewKey, TransitionType transition) 
            where TView : INavigableView
        {
            Check.Require(viewKey != null, "viewKey cannot be null");
            INavigableView view;
            _views.TryGetValue(viewKey, out view);

            if (view == null)
            {
                view = RegisterView(viewKey);
            }
            else if (ReferenceEquals(_currentView, view))
            {
                return;
            }

            _transitioner.Transition(_currentView, view, transition);

            if (_currentView != null)
            {
                _contentRegionOwner.RemoveView(_currentView);
            }
            _currentView = view;
        }

        private INavigableView RegisterView<TView>(ViewKey<TView> viewKey)
            where TView : INavigableView
        {
            var view = _viewFactory.Get<TView>();
            if (view.ViewModel != null)
            {
                view.ViewModel.ViewKey = viewKey;
            }
            _views.Add(viewKey, view);
            _contentRegionOwner.AddView(view);
            _eventPublisher.Publish(new ViewAddedEvent(viewKey, view));

            return view;
        }

        public void TransitionTo(ViewKey viewKey, TransitionType transition)
        {
            INavigableView view;
            _views.TryGetValue(viewKey, out view);
            Check.Ensure(view != null, "View not found by ViewManager");

            if (ReferenceEquals(_currentView, view))
            {
                return;
            }

            _transitioner.Transition(_currentView, view, transition);
            if (_currentView != null)
            {
                _contentRegionOwner.RemoveView(_currentView);
            }
            _currentView = view;
        }

        public void Remove(ViewKey viewKey, TransitionType transition)
        {
            Check.Require(viewKey != null, "viewKey cannot be null");

            if (!ViewCanClose(viewKey)) return;

            if (viewKey.Parent != null)
            {
                TransitionTo(viewKey.Parent, transition);
            }
            else
            {
                TransitionToHome();
            }

            CloseView(viewKey);
        }

        private bool ViewChildrenCanClose(ViewKey parent)
        {
            var children = _views.Where(v => v.Key.Parent == parent);
            if (children.Any())
            {
                if (children.Any(child => !ViewCanClose(child.Key)))
                {
                    return false;
                }
            }

            return true;
        }

        private bool ViewCanClose(ViewKey key)
        {
            bool canClose = true;
            var viewModel = _views[key].ViewModel;

            if (viewModel != null && !viewModel.CanClose())
            {
                TransitionTo(key, TransitionType.None);
                canClose = viewModel.PropmtUserForClose();
            }

            return !canClose || ViewChildrenCanClose(key);
        }

        private void CloseView(ViewKey key)
        {
            var view = _views[key];
            foreach (var child in _views.Keys.Where(k => k.Parent == key))
            {
                CloseView(child);
            }
            _contentRegionOwner.RemoveView(view);
            _views.Remove(key);
            view.Dispose();
        }

        public void TransitionToHome()
        {
            if (_homeTransitionAction != null)
            {
                _homeTransitionAction();
            }
        }
    }
}
